/*
 * Lenovo Group
 * Copyright (c) 2015-2016 All Rights Reserved.
 * 
 * Project Name:itsm-gateway
 * Create Time: 2016年3月3日 上午9:50:44
 */
package com.lenovo.lmrp.server.itsmgateway.spi;

import java.io.InputStream;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.jboss.resteasy.plugins.guice.RequestScoped;

import com.lenovo.lmrp.api.itsmgateway.IItsmStream;
import com.lenovo.lmrp.api.model.RestResponse;
import com.lenovo.lmrp.base.IFileService;
import com.lenovo.lmrp.server.itsmgateway.adapter.AItsmAdapterFactory;
import com.lenovo.lmrp.server.itsmgateway.domain.metadata.ItsmEntityTranslator.EntityName;
import com.lenovo.lmrp.server.itsmgateway.domain.ticket.AttachmentBO;
import com.lenovo.lmrp.server.itsmgateway.repository.ARepositoryFactory;
import com.lenovo.sdc.framework.annotation.RestService;
import com.lenovo.sdc.framework.util.CommonUtil;
import com.lenovo.sdc.framework.util.Tuple;
import com.lenovo.sdc.framework.util.exception.ErrorCode;
import com.lenovo.sdc.framework.util.exception.TraceableException;

/**
 * 
 * @author wujin
 * @version v1.0
 */
@RestService
@Path("/itsm/v1")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces("application/json;charset-utf-8")
@RequestScoped
public class ItsmStreamImpl implements IItsmStream {
    private HttpServletRequest request;
    private AItsmAdapterFactory adapterFactory;
    private ARepositoryFactory repositoryFactory;
    private IFileService fileService;

    private ThreadPoolExecutor executorPool;

    public ItsmStreamImpl() {
        executorPool = new ThreadPoolExecutor(4, 8, 30, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>());
    }

    @Inject
    public void set(ARepositoryFactory repositoryFactory) {
        this.repositoryFactory = repositoryFactory;
    }

    @Inject
    public void set(AItsmAdapterFactory adapterFactory) {
        this.adapterFactory = adapterFactory;
    }

    @Inject
    public void set(IFileService fileService) {
        this.fileService = fileService;
    }

    @Inject
    public void set(HttpServletRequest request) {
        this.request = request;
    }
    
    /**
     * Invoker should use createAttachment to create Attachment basic
     * information then use this method to upload file content.
     * 
     * This method use another aysnc thread to receive input stream, after
     * complete read file and saved, then send back response to notify cleint.
     * ITSM Gateway has another scheduler thread to sync file to ITSM
     */
    @Override
    public void uploadAttachment(String itsmcode, String ticketid, String attachmentid, AsyncResponse asyncResponse) {
        executorPool.execute(new Runnable() {
            public void run() {
                try {
                    AttachmentBO bo = repositoryFactory.getTicketRepository()
                        .createAttachmentBO(itsmcode, attachmentid);
                    if (Objects.isNull(bo)) {
                        asyncResponse.resume(Response
                            .status(404)
                            .entity(new RestResponse<Integer>(
                                            ErrorCode.DB_RECORD_NOTFOUND.getCode(), 
                                            "may not create " + attachmentid, 
                                            500))
                            .build());
                        return;
                    }

                    Collection<Part> parts = request.getParts();
                    for (Part part : parts) {
                        String header = part.getHeader("content-disposition");
                        if (Objects.nonNull(part.getContentType()) && header.contains("filename=\"")) {
                            // Servlet3 doesn't provide method to access file name
                            // directly, we should parse from request header
                            // request header format: form-data; name="xxx";
                            // filename="xxxxx"
                            bo.saveContent(getFileName(header), fileService, part.getInputStream());
                            part.getInputStream().close();
                            bo.store(repositoryFactory.getTicketRepository());
                        }
                    }
                    asyncResponse.resume(Response.ok(new RestResponse<Integer>(0, "OK", 0)).build());
                }
                catch (Exception exp) {
                    TraceableException.writeLog(ErrorCode.FILE_READ_FAILED, itsmcode + ":" + attachmentid, exp);
                }
            }
        });
    }

    @Override
    public void importEntityData(String itsmCode, String entity, AsyncResponse asyncResponse) {
        executorPool.execute(new Runnable() {
            public void run() {
                try {
                    Collection<Part> parts = request.getParts();
                    for (Part part : parts) {
                        String header = part.getHeader("content-disposition");
                        System.out.println(header + ":" + part.getContentType());
                        if (Objects.nonNull(part.getContentType()) && header.contains("filename=\"")) {

                            InputStream input = part.getInputStream();
                            Tuple.Pair<Boolean, String> result = adapterFactory.getAdapter(itsmCode).importEntityData(
                                EntityName.valueOf(entity.toUpperCase()), input);
                            CommonUtil.closeIO(input);
                            if (result.getOne())
                                continue;
                            else {
                                asyncResponse.resume(Response
                                    .status(500)
                                    .entity(new RestResponse<Integer>(
                                                        ErrorCode.FILE_READ_FAILED.getCode(), 
                                                        result.getSecond(),
                                                        500))
                                    .build());
                                return;
                            }
                        }
                    }
                    asyncResponse.resume(Response.ok(new RestResponse<Integer>(0, "OK", 0)).build());
                }
                catch (Exception exp) {
                    TraceableException.writeLog(ErrorCode.FILE_READ_FAILED, "", exp);
                    asyncResponse.resume(Response
                        .status(500)
                        .entity(new RestResponse<Integer>(
                                        ErrorCode.COMM_ERROR.getCode(), 
                                        exp.getMessage(),
                                        500))
                        .build());
                }
            }
        });
    }

    private String getFileName(String header) {
        /**
         * String[] tempArr1 =
         * header.split(";");代码执行完之后，在不同的浏览器下，tempArr1数组里面的内容稍有区别
         * 火狐或者google浏览器下：tempArr1={form-data;name="file";filename="snmp4j--api.zip"}
         * IE浏览器下：tempArr1={form-data;name="file";filename="E:\snmp4j--api.zip"}
         */
        String[] tempArr1 = header.split(";");
        /**
         * 火狐或者google浏览器下：tempArr2={filename,"snmp4j--api.zip"}
         * IE浏览器下：tempArr2={filename,"E:\snmp4j--api.zip"}
         */
        String[] tempArr2 = tempArr1[2].split("=");
        // 获取文件名，兼容各种浏览器的写法
        String fileName = tempArr2[1].substring(tempArr2[1].lastIndexOf("\\") + 1).replaceAll("\"", "");
        return fileName;
    }
}
